﻿using System;
using System.Collections.Generic;
using oqOrder = OpenQuant.API.Order;
using ssOrder = StockSharp.BusinessEntities.Order;

namespace OpenQuant.Quik
{
    /// <summary>
    /// Хранилище заявок провайдера OpenQuant.Quik
    /// </summary>
    public class OrderHive
    {
        /// <summary>
        /// Тип приказа
        /// </summary>
        public enum OrderHiveType
        {
            /// <summary>
            /// Лимитированный приказ
            /// </summary>
            Limit,
            /// <summary>
            /// Стоп-приказ
            /// </summary>
            Stop
        }
        
        readonly Dictionary<long, ssOrder> _orders = new Dictionary<long, ssOrder>();
        readonly Dictionary<oqOrder, ssOrder> _orders1 = new Dictionary<oqOrder, ssOrder>();
        readonly Dictionary<ssOrder, oqOrder> _orders2 = new Dictionary<ssOrder, oqOrder>();
        readonly Dictionary<long, ssOrder> _stopOrders = new Dictionary<long, ssOrder>();
        readonly Dictionary<oqOrder, ssOrder> _stopOrders1 = new Dictionary<oqOrder, ssOrder>();
        readonly Dictionary<ssOrder, oqOrder> _stopOrders2 = new Dictionary<ssOrder, oqOrder>();

        void WithDictionaries(OrderHiveType type, Action<Dictionary<long, ssOrder>, Dictionary<oqOrder, ssOrder>, Dictionary<ssOrder, oqOrder>> todo)
        {
            switch (type)
            {
                case OrderHiveType.Limit:
                    todo(_orders, _orders1, _orders2);
                    break;
                case OrderHiveType.Stop:
                    todo(_stopOrders, _stopOrders1, _stopOrders2);
                    break;
                default:
                    throw new NotSupportedException();
            }
        }
        
        /// <summary>
        /// Установить в хранилище пару заявок OpenQuant и S#
        /// </summary>
        /// <param name="transactionId">ID транзакции, в случае, когда приказ сформирован, но ID транзакции еще не присвоено S#.
        /// Выбирается следующий номер в IncrementTransactionIdGenerator</param>
        /// <param name="quant">Приказ OpenQuant</param>
        /// <param name="quik">Приказ S#</param>
        public void Set(long transactionId, oqOrder quant, ssOrder quik)
        {
            if (transactionId <= 0) throw new ArgumentOutOfRangeException();
            if (quant == null) throw new ArgumentNullException("quant");
            if (quik == null) throw new ArgumentNullException("quik");

            if (quik.Condition == null)
            {
                _orders[transactionId] = quik;
                _orders1[quant] = quik;
                _orders2[quik] = quant;
            }
            else
            {
                _stopOrders[transactionId] = quik;
                _stopOrders1[quant] = quik;
                _stopOrders2[quik] = quant;
            }
        }

        /// <summary>
        /// Установить в хранилище пару заявок OpenQuant и S#
        /// </summary>
        /// <param name="quant">Приказ OpenQuant</param>
        /// <param name="quik">Приказ S#</param>
        public void Set(oqOrder quant, ssOrder quik)
        {
            if (quant == null) throw new ArgumentNullException("quant");
            if (quik == null) throw new ArgumentNullException("quik");

            Set(quik.TransactionId, quant, quik);
        }

        /// <summary>
        /// Получить приказ S# по ID транзакции приказа S# и типу заявок хранилища
        /// </summary>
        /// <param name="transactionId">ID транзакции приказа S#</param>
        /// <param name="type">Тип приказа</param>
        /// <returns></returns>
        public ssOrder GetQuik(long transactionId, OrderHiveType type)
        {
            ssOrder rst = null;
            WithDictionaries(type, (s, s1, s2) => rst = s[transactionId]);
            return rst;
        }

        /// <summary>
        /// Получить приказ S# по приказу OpenQuant и типу заявок хранилища
        /// </summary>
        /// <param name="quant">Приказ OpenQuant</param>
        /// <param name="type">Тип приказа</param>
        /// <returns></returns>
        public ssOrder GetQuik(oqOrder quant, OrderHiveType type)
        {
            if (quant == null) throw new ArgumentNullException("quant");

            ssOrder rst = null;
            WithDictionaries(type, (s, s1, s2) => rst = s1[quant]);
            return rst;
        }

        /// <summary>
        /// Получить приказ OpenQuant по ID транзакции приказа S#
        /// </summary>
        /// <param name="transactionId">ID транзакции приказа S#</param>
        /// <param name="type">Тип приказа</param>
        /// <returns></returns>
        public oqOrder GetQuant(long transactionId, OrderHiveType type)
        {
            oqOrder rst = null;
            WithDictionaries(type, (s, s1, s2) => rst = s2[s[transactionId]]);
            return rst;
        }

        /// <summary>
        /// Возвращает значение, указыващиее найдены ли приказы OpenQuant и S# в хранилище
        /// </summary>
        /// <param name="transactionId">ID транзакции приказа S#</param>
        /// <param name="type">Тип приказа</param>
        /// <returns></returns>
        public bool TrySearch(long transactionId, out OrderHiveType type)
        {
            ssOrder quik;
            if (_orders.TryGetValue(transactionId, out quik))
            {
                type = OrderHiveType.Limit;
                return true;
            }
            if (_stopOrders.TryGetValue(transactionId, out quik))
            {
                type = OrderHiveType.Stop;
                return true;
            }
            type = OrderHiveType.Limit;
            return false;
        }

        /// <summary>
        /// Возвращает значение, указыващиее найдены ли приказы OpenQuant и S# в хранилище
        /// </summary>
        /// <param name="quant">Приказ OpenQuant</param>
        /// <param name="type">Тип приказа</param>
        /// <returns></returns>
        public bool TrySearch(oqOrder quant, out OrderHiveType type)
        {
            ssOrder quik;
            if (_orders1.TryGetValue(quant, out quik))
            {
                type = OrderHiveType.Limit;
                return true;
            }
            if (_stopOrders1.TryGetValue(quant, out quik))
            {
                type = OrderHiveType.Stop;
                return true;
            }
            type = OrderHiveType.Limit;
            return false;
        }

        /// <summary>
        /// Удалить информацию о приказах из хранилища
        /// </summary>
        /// <param name="tramsactionId">ID транзакции приказа S#</param>
        /// <param name="type">Тип приказа</param>
        public void Remove(long tramsactionId, OrderHiveType type)
        {
            WithDictionaries(type, (s, s1, s2) =>
                {
                    if (!s.ContainsKey(tramsactionId)) return;
                    var quik = s[tramsactionId];
                    var quant = s2[quik];
                    s.Remove(tramsactionId);
                    s1.Remove(quant);
                    s2.Remove(quik);
                });
        }

        /// <summary>
        /// Удалить информацию о приказах из хранилища
        /// </summary>
        /// <param name="quant">Приказ OpenQuant</param>
        /// <param name="type">Тип приказа</param>
        public void Remove(oqOrder quant, OrderHiveType type)
        {
            WithDictionaries(type, (s, s1, s2) =>
            {
                if (!s1.ContainsKey(quant)) return;
                var quik = s1[quant];
                s.Remove(quik.TransactionId);
                s1.Remove(quant);
                s2.Remove(quik);
            });
        }
    }
}
